#ifndef THREADPOOL_H
#define THREADPOOL_H

#include <mutex>
#include <condition_variable>
#include <queue>
#include <thread>
#include <functional>
#include <assert.h>

/* 
    完整的任务执行：
    调用AddTask向任务队列添加任务，并notify_one唤醒一个线程，使得在条件变量处等待的一个线程继续往下执行，
    重新开始while循环，把任务从队列中取出，并执行。
    对变量进行操作时均要加锁
*/

class ThreadPool {
public:
    // make_shared：为智能指针在堆上分配空间
    explicit ThreadPool(size_t threadCount = 8): m_Pool(std::make_shared<Pool>()) {
        assert(threadCount > 0); // assert为true则不操作，为false则显示出错信息并使程序崩溃
        for (size_t i = 0; i < threadCount; ++i) {
            // thread()中的是匿名函数，把m_Pool当作参数传入
            std::thread([pool = m_Pool] {
                std::unique_lock<std::mutex> locker(pool->mtx);
                while(true) {
                    if (!pool->tasks.empty()) {// 队列不空时再进行操作
                        auto task = std::move(pool->tasks.front()); // 获得任务队列中最前面的任务，使用move只进行所有权转移，不进行数据复制,注意所有权转移后，再访问原有变量是未定义的行为
                        pool->tasks.pop(); // 从队列中把此任务删除
                        locker.unlock(); // 接下来要执行任务，执行任务时其它线程可以操作任务队列，所以要解锁
                        task();
                        locker.lock(); // 获得锁，保证下次循环开始时持有锁
                    } else if (pool->isClosed) {
                        break;
                    } else {
                        // 如果队列为空，就使用条件变量等待，在此期间释放locker，并休眠
                        pool->cond.wait(locker); 
                        // condition.wait(unique_lock, 函数(匿名函数))，返回true时继续向下执行，返回false则解锁并进入阻塞，不占用cpu时间
                        // 直到别的线程调用notify，被唤醒后，尝试锁定locker，若锁定成功，若未提供谓词（第二个参数），则会直接
                        // 继续执行，若提供了，会再判断谓词是否返回true，重复上述过程。
                    }
                }
            }).detach(); // 让线程独立执行，线程可以自行清理资源，join有阻塞的作用
        }
    }

    ThreadPool() = default; // 使用默认的生成函数
    ThreadPool(ThreadPool&&) = default; // 使用默认的移动构造函数

    ~ThreadPool() {
        // 使用static_cast判断m_Pool是否为空
        if (static_cast<bool>(m_Pool)) {
            {
                std::lock_guard<std::mutex> locker(m_Pool->mtx);
                m_Pool->isClosed = true;
            }

            // 唤醒所有线程，退出循环
            m_Pool->cond.notify_all();
        }
    }

    // 对传入对象的完美转发，不管是左值还是右值都能正确传递，这样写可以接受匿名函数
    template<typename T>
    void AddTask(T&& task) {
        {
            std::lock_guard<std::mutex> locker(m_Pool->mtx);
            m_Pool->tasks.emplace(std::forward<T>(task)); // forward主要是为了保留原始值类别，实现完美转发，避免不必要的拷贝或移动操作
        }
        m_Pool->cond.notify_one();
    }
private:
    struct Pool {
        std::mutex mtx; // 互斥量
        std::condition_variable cond; // 条件变量
        bool isClosed; // 线程池是否关闭
        std::queue<std::function<void()>> tasks; // std::function:函数包装模板， tasks:一个队列，保存任务
    };
    std::shared_ptr<Pool> m_Pool; // 智能指针，线程池
};


#endif